texture: Add gdk_texture_download_float()
authorBenjamin Otte <otte@redhat.com>
Fri, 10 Sep 2021 00:40:21 +0000 (02:40 +0200)
committerBenjamin Otte <otte@redhat.com>
Sun, 12 Sep 2021 03:54:37 +0000 (05:54 +0200)
gdk/gdkgltexture.c
gdk/gdkmemorytexture.c
gdk/gdkmemorytextureprivate.h
gdk/gdktexture.c
gdk/gdktexture.h
gdk/gdktextureprivate.h

index bd69809061c4575edaf986eae9a57168b395c3b8..365a3a6ac2c851a653868f1cf3aa68752a342f45 100644 (file)
@@ -179,6 +179,60 @@ gdk_gl_texture_download (GdkTexture *texture,
   glBindTexture (GL_TEXTURE_2D, active_texture);
 }
 
+static void
+gdk_gl_texture_do_download_float (GdkTexture *texture,
+                                  float      *data)
+{
+  GdkGLTexture *self = GDK_GL_TEXTURE (texture);
+  int active_texture;
+
+  gdk_gl_context_make_current (self->context);
+
+  glGetIntegerv (GL_TEXTURE_BINDING_2D, &active_texture);
+  glBindTexture (GL_TEXTURE_2D, self->id);
+
+  glGetTexImage (GL_TEXTURE_2D,
+                 0,
+                 GL_RGBA,
+                 GL_FLOAT,
+                 data);
+
+  glBindTexture (GL_TEXTURE_2D, active_texture);
+}
+
+static void
+gdk_gl_texture_download_float (GdkTexture *texture,
+                               float      *data,
+                               gsize       stride)
+{
+  GdkGLTexture *self = GDK_GL_TEXTURE (texture);
+  int width, height, y;
+  float *copy;
+
+  if (self->saved)
+    {
+      gdk_texture_download_float (self->saved, data, stride);
+      return;
+    }
+
+  width = gdk_texture_get_width (texture);
+  height = gdk_texture_get_height (texture);
+
+  if (stride == width * 4)
+    {
+      gdk_gl_texture_do_download_float (texture, data);
+      return;
+    }
+
+  copy = g_new (float, width * height * 4);
+
+  gdk_gl_texture_do_download_float (texture, copy);
+  for (y = 0; y < height; y++)
+    memcpy (data + y * stride, copy + y * 4 * width, 4 * width);
+
+  g_free (copy);
+}
+
 static void
 gdk_gl_texture_class_init (GdkGLTextureClass *klass)
 {
@@ -187,6 +241,7 @@ gdk_gl_texture_class_init (GdkGLTextureClass *klass)
 
   texture_class->download_texture = gdk_gl_texture_download_texture;
   texture_class->download = gdk_gl_texture_download;
+  texture_class->download_float = gdk_gl_texture_download_float;
   gobject_class->dispose = gdk_gl_texture_dispose;
 }
 
index edb4d741c608244a3e139a3d622e9a32a2648af0..6d0c861eecd499b61956640c93fe703b99accdb3 100644 (file)
@@ -101,6 +101,21 @@ gdk_memory_texture_download (GdkTexture *texture,
                       gdk_texture_get_height (texture));
 }
 
+static void
+gdk_memory_texture_download_float (GdkTexture *texture,
+                                   float      *data,
+                                   gsize       stride)
+{
+  GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
+
+  gdk_memory_convert_to_float (data, stride,
+                               (guchar *) g_bytes_get_data (self->bytes, NULL),
+                               self->stride,
+                               self->format,
+                               gdk_texture_get_width (texture),
+                               gdk_texture_get_height (texture));
+}
+
 static void
 gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
 {
@@ -109,6 +124,7 @@ gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
 
   texture_class->download_texture = gdk_memory_texture_download_texture;
   texture_class->download = gdk_memory_texture_download;
+  texture_class->download_float = gdk_memory_texture_download_float;
   gobject_class->dispose = gdk_memory_texture_dispose;
 }
 
@@ -323,3 +339,82 @@ gdk_memory_convert (guchar              *dest_data,
 
   converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height);
 }
+
+#define CONVERT_FLOAT(R,G,B,A,premultiply) G_STMT_START {\
+  for (y = 0; y < height; y++) \
+    { \
+      for (x = 0; x < width; x++) \
+        { \
+          if (A >= 0) \
+            { \
+              dest_data[4 * x + 0] = src_data[4 * x + R] / 255.0f; \
+              dest_data[4 * x + 1] = src_data[4 * x + G] / 255.0f; \
+              dest_data[4 * x + 2] = src_data[4 * x + B] / 255.0f; \
+              dest_data[4 * x + 3] = src_data[4 * x + A] / 255.0f; \
+              if (premultiply) \
+                { \
+                  dest_data[4 * x + 0] *= dest_data[4 * x + 3]; \
+                  dest_data[4 * x + 1] *= dest_data[4 * x + 3]; \
+                  dest_data[4 * x + 2] *= dest_data[4 * x + 3]; \
+                } \
+            } \
+          else \
+            { \
+              dest_data[4 * x + 0] = src_data[3 * x + R] / 255.0f; \
+              dest_data[4 * x + 1] = src_data[3 * x + G] / 255.0f; \
+              dest_data[4 * x + 2] = src_data[3 * x + B] / 255.0f; \
+              dest_data[4 * x + 3] = 1.0; \
+            } \
+        } \
+\
+      dest_data += dest_stride; \
+      src_data += src_stride; \
+    } \
+}G_STMT_END
+
+void
+gdk_memory_convert_to_float (float           *dest_data,
+                             gsize            dest_stride,
+                             const guchar    *src_data,
+                             gsize            src_stride,
+                             GdkMemoryFormat  src_format,
+                             gsize            width,
+                             gsize            height)
+{
+  gsize x, y;
+
+  switch (src_format)
+  {
+    case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
+      CONVERT_FLOAT (2, 1, 0, 3, FALSE);
+      break;
+    case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
+      CONVERT_FLOAT (1, 2, 3, 0, FALSE);
+      break;
+    case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
+      CONVERT_FLOAT (0, 1, 2, 3, FALSE);
+      break;
+    case GDK_MEMORY_B8G8R8A8:
+      CONVERT_FLOAT (2, 1, 0, 3, TRUE);
+      break;
+    case GDK_MEMORY_A8R8G8B8:
+      CONVERT_FLOAT (1, 2, 3, 0, TRUE);
+      break;
+    case GDK_MEMORY_R8G8B8A8:
+      CONVERT_FLOAT (0, 1, 2, 3, TRUE);
+      break;
+    case GDK_MEMORY_A8B8G8R8:
+      CONVERT_FLOAT (3, 2, 1, 0, TRUE);
+      break;
+    case GDK_MEMORY_R8G8B8:
+      CONVERT_FLOAT (0, 1, 2, -1, FALSE);
+      break;
+    case GDK_MEMORY_B8G8R8:
+      CONVERT_FLOAT (2, 1, 0, -1, FALSE);
+      break;
+
+    case GDK_MEMORY_N_FORMATS:
+    default:
+      g_assert_not_reached();
+  }
+}
index a450a9a139c2aa7bdec572ac46f0a4a4dff49ab5..ddd9fd1c37ee9b7451c845d85ec833b217a97b50 100644 (file)
@@ -60,6 +60,14 @@ void                    gdk_memory_convert                  (guchar
                                                              gsize                       width,
                                                              gsize                       height);
 
+void                    gdk_memory_convert_to_float         (float                      *dest_data,
+                                                             gsize                       dest_stride,
+                                                             const guchar               *src_data,
+                                                             gsize                       src_stride,
+                                                             GdkMemoryFormat             src_format,
+                                                             gsize                       width,
+                                                             gsize                       height);
+
 
 G_END_DECLS
 
index 4afd6ebfdd28ac642aea56a1d258f7e3bfa04de5..7b055238fd39084ed640e83351996935b5b128c9 100644 (file)
@@ -134,6 +134,18 @@ gdk_texture_real_download (GdkTexture *texture,
   g_object_unref (memory_texture);
 }
 
+static void
+gdk_texture_real_download_float (GdkTexture *self,
+                                 float      *data,
+                                 gsize       stride)
+{
+  GdkTexture *memory_texture;
+
+  memory_texture = gdk_texture_download_texture (self);
+  gdk_texture_download_float (memory_texture, data, stride);
+  g_object_unref (memory_texture);
+}
+
 static void
 gdk_texture_set_property (GObject      *gobject,
                           guint         prop_id,
@@ -199,6 +211,7 @@ gdk_texture_class_init (GdkTextureClass *klass)
 
   klass->download_texture = gdk_texture_real_download_texture;
   klass->download = gdk_texture_real_download;
+  klass->download_float = gdk_texture_real_download_float;
 
   gobject_class->set_property = gdk_texture_set_property;
   gobject_class->get_property = gdk_texture_get_property;
@@ -485,6 +498,45 @@ gdk_texture_download (GdkTexture *texture,
   GDK_TEXTURE_GET_CLASS (texture)->download (texture, data, stride);
 }
 
+/**
+ * gdk_texture_download_float:
+ * @texture: a `GdkTexture`
+ * @data: (array): pointer to enough memory to be filled with the
+ *   downloaded data of @texture
+ * @stride: rowstride in elements, will usually be equal to
+ *   gdk_texture_get_width() * 4
+ *
+ * Downloads the @texture into local memory in a high dynamic range format.
+ *
+ * This may be an expensive operation, as the actual texture data
+ * may reside on a GPU or on a remote display server and because the data
+ * may need to be upsampled if it was not already available in this
+ * format.
+ *
+ * You may want to use [method@Gdk.Texture.download] instead if you don't
+ * need high dynamic range support.
+ *
+ * The data format of the downloaded data is equivalent to
+ * GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED, so every downloaded
+ * pixel requires 16 bytes of memory.
+ *
+ * Note that the caller is responsible to provide sufficiently
+ * aligned memory to access the resulting data directly as floats.
+ *
+ * Since: 4.6
+ */
+void
+gdk_texture_download_float (GdkTexture *texture,
+                            float      *data,
+                            gsize       stride)
+{
+  g_return_if_fail (GDK_IS_TEXTURE (texture));
+  g_return_if_fail (data != NULL);
+  g_return_if_fail (stride >= gdk_texture_get_width (texture) * 4);
+
+  GDK_TEXTURE_GET_CLASS (texture)->download_float (texture, data, stride);
+}
+
 GdkTexture *
 gdk_texture_download_texture (GdkTexture *texture)
 {
index f3d0bc976559fca58c4a88e23f06b2b99e498fe2..9e3b7a93fa3d74bd9822c7057d6ece7cc1df4bfe 100644 (file)
@@ -59,6 +59,10 @@ GDK_AVAILABLE_IN_ALL
 void                    gdk_texture_download                   (GdkTexture      *texture,
                                                                 guchar          *data,
                                                                 gsize            stride);
+GDK_AVAILABLE_IN_4_6
+void                    gdk_texture_download_float             (GdkTexture      *texture,
+                                                                float           *data,
+                                                                gsize            stride);
 GDK_AVAILABLE_IN_ALL
 gboolean                gdk_texture_save_to_png                (GdkTexture      *texture,
                                                                 const char      *filename);
index 1133bf6acc17335214fc1decd61faa1cd5e4317b..29fca0a9ba5b0042170f72ab8ae39ddd0666b056 100644 (file)
@@ -30,6 +30,9 @@ struct _GdkTextureClass {
   void                  (* download)                    (GdkTexture             *texture,
                                                          guchar                 *data,
                                                          gsize                   stride);
+  void                  (* download_float)              (GdkTexture             *texture,
+                                                         float                  *data,
+                                                         gsize                   stride);
 };
 
 gpointer                gdk_texture_new                 (const GdkTextureClass  *klass,